import os
import sys
import json
import tempfile
import subprocess
import re
import uvicorn
from typing import Optional, List
from datetime import datetime, timedelta
from fastapi import FastAPI, HTTPException, Depends, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel, EmailStr
from jose import JWTError, jwt
from passlib.context import CryptContext

# 对齐 inference 的镜像环境
os.environ.setdefault("HF_ENDPOINT", "https://hf-mirror.com")
os.environ.setdefault("HF_HUB_BASE_URL", os.environ["HF_ENDPOINT"])
os.environ.setdefault("HUGGINGFACE_HUB_BASE_URL", os.environ["HF_ENDPOINT"])

# 将项目根目录加入 sys.path,便于从 backend/ 下导入仓库根模块
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if BASE_DIR not in sys.path:
    sys.path.insert(0, BASE_DIR)

# 导入数据库操作
from backend.database import (
    init_db,
    create_user,
    authenticate_user,
    get_user_by_username,
    save_conversation,
    get_user_conversations,
    get_conversation_count,
    delete_conversation,
    clear_user_conversations,
    get_user_stats,
    update_user_stats,
    get_user_achievements,
)

# 复用推理逻辑(模板、few-shot、函数校验等)
from utils.inference import (
    generate_code,
    validate_python_code,
    looks_like_function,
)
from utils.code_templates import get_template_for_prompt
import re
import ast

# JWT 配置
SECRET_KEY = os.getenv("JWT_SECRET_KEY", "your-secret-key-change-in-production")
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 60 * 24 * 7  # 7天

security = HTTPBearer()

app = FastAPI(title="Text2Code API", version="1.0.0")

# CORS 配置,允许前端跨域访问
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # 生产环境应指定具体域名
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


# ===================== #
# 认证相关模型和工具函数
# ===================== #

class UserRegister(BaseModel):
    username: str
    email: EmailStr
    password: str


class UserLogin(BaseModel):
    username: str
    password: str


class Token(BaseModel):
    access_token: str
    token_type: str
    username: str
    user_id: int


class UserInfo(BaseModel):
    id: int
    username: str
    email: str
    created_at: str


def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
    """创建JWT访问令牌"""
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=15)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt


async def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security)):
    """验证JWT令牌并返回当前用户"""
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="无效的认证凭据",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        token = credentials.credentials
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise credentials_exception
    except JWTError:
        raise credentials_exception
    
    user = get_user_by_username(username)
    if user is None:
        raise credentials_exception
    return user


# ===================== #
# 认证相关API
# ===================== #

@app.post("/auth/register", response_model=Token)
async def register(user_data: UserRegister):
    """用户注册"""
    # 检查用户名是否已存在
    if get_user_by_username(user_data.username):
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="用户名已存在"
        )
    
    # 创建用户
    user_id = create_user(user_data.username, user_data.email, user_data.password)
    if user_id is None:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="注册失败,邮箱可能已被使用"
        )
    
    # 生成JWT令牌
    access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    access_token = create_access_token(
        data={"sub": user_data.username},
        expires_delta=access_token_expires
    )
    
    return Token(
        access_token=access_token,
        token_type="bearer",
        username=user_data.username,
        user_id=user_id
    )


@app.post("/auth/login", response_model=Token)
async def login(user_data: UserLogin):
    """用户登录"""
    user = authenticate_user(user_data.username, user_data.password)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="用户名或密码错误",
            headers={"WWW-Authenticate": "Bearer"},
        )
    
    # 生成JWT令牌
    access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    access_token = create_access_token(
        data={"sub": user["username"]},
        expires_delta=access_token_expires
    )
    
    return Token(
        access_token=access_token,
        token_type="bearer",
        username=user["username"],
        user_id=user["id"]
    )


@app.get("/auth/me", response_model=UserInfo)
async def get_me(current_user: dict = Depends(get_current_user)):
    """获取当前用户信息"""
    return UserInfo(
        id=current_user["id"],
        username=current_user["username"],
        email=current_user["email"],
        created_at=current_user["created_at"]
    )


# ===================== #
# 对话历史相关模型和API
# ===================== #

class ConversationItem(BaseModel):
    id: int
    prompt: str
    generated_code: str
    is_valid: bool
    created_at: str


class ConversationList(BaseModel):
    conversations: List[ConversationItem]
    total: int
    page: int
    page_size: int


@app.get("/conversations", response_model=ConversationList)
async def get_conversations(
    page: int = 1,
    page_size: int = 20,
    current_user: dict = Depends(get_current_user)
):
    """获取当前用户的对话历史(分页)"""
    offset = (page - 1) * page_size
    conversations = get_user_conversations(current_user["id"], limit=page_size, offset=offset)
    total = get_conversation_count(current_user["id"])
    
    return ConversationList(
        conversations=[ConversationItem(**conv) for conv in conversations],
        total=total,
        page=page,
        page_size=page_size
    )


@app.delete("/conversations/{conv_id}")
async def delete_conv(conv_id: int, current_user: dict = Depends(get_current_user)):
    """删除指定对话记录"""
    success = delete_conversation(conv_id, current_user["id"])
    if not success:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="对话记录不存在或无权删除"
        )
    return {"message": "删除成功"}


@app.delete("/conversations")
async def clear_conversations(current_user: dict = Depends(get_current_user)):
    """清空所有对话历史"""
    deleted = clear_user_conversations(current_user["id"])
    return {"message": f"已清空 {deleted} 条对话记录"}


def explain_code(code: str) -> str:
    """生成代码的自然语言解释"""
    explanation = []
    
    try:
        # 解析代码
        tree = ast.parse(code)
        
        # 提取函数信息
        for node in ast.walk(tree):
            if isinstance(node, ast.FunctionDef):
                func_name = node.name
                params = [arg.arg for arg in node.args.args]
                
                explanation.append(f"📝 函数名: {func_name}")
                if params:
                    explanation.append(f"📥 参数: {', '.join(params)}")
                else:
                    explanation.append("📥 无参数")
                
                # 分析函数体
                if node.body:
                    explanation.append(f"📊 共 {len(node.body)} 条语句")
                    
                    # 检测返回值
                    has_return = any(isinstance(n, ast.Return) for n in ast.walk(node))
                    if has_return:
                        explanation.append("📤 有返回值")
                    
                    # 检测循环
                    loops = [n for n in ast.walk(node) if isinstance(n, (ast.For, ast.While))]
                    if loops:
                        explanation.append(f"🔁 使用了 {len(loops)} 个循环")
                    
                    # 检测条件判断
                    ifs = [n for n in ast.walk(node) if isinstance(n, ast.If)]
                    if ifs:
                        explanation.append(f"🔀 包含 {len(ifs)} 个条件判断")
                    
                    # 检测列表推导
                    comprehensions = [n for n in ast.walk(node) if isinstance(n, (ast.ListComp, ast.DictComp, ast.SetComp))]
                    if comprehensions:
                        explanation.append(f"⚡ 使用了 {len(comprehensions)} 个推导式")
        
        # 如果没有解析到函数,使用简单分析
        if not explanation:
            explanation.append("🔍 这是一段Python代码")
            if 'def ' in code:
                explanation.append("📝 定义了函数")
            if 'return' in code:
                explanation.append("📤 包含返回语句")
    
    except SyntaxError:
        explanation.append("⚠️ 代码存在语法错误,无法完整分析")
        # 简单的关键词分析
        if 'def ' in code:
            explanation.append("📝 尝试定义函数")
        if 'for' in code or 'while' in code:
            explanation.append("🔁 包含循环结构")
        if 'if' in code:
            explanation.append("🔀 包含条件判断")
    
    return "\n".join(explanation) if explanation else "❓ 无法分析此代码"


# ===================== #
# 代码生成相关模型和API
# ===================== #

class GenReq(BaseModel):
    prompt: str
    max_new_tokens: int = 256
    save_history: bool = True  # 是否保存到历史记录
    mode: str | None = "auto"  # 推理方向：auto|simple|complex|merged


class GenResp(BaseModel):
    code: str
    valid_syntax: bool
    looks_like_function: bool
    conversation_id: Optional[int] = None  # 保存的对话记录ID
    # 评分逻辑已移除


# 评分模型已移除


class InvokeSpec(BaseModel):
    function_name: str
    args: Optional[List] = None


class ExecReq(BaseModel):
    code: str
    test_snippet: Optional[str] = None
    invoke: Optional[InvokeSpec] = None
    timeout_sec: int = 10


class ErrorInfo(BaseModel):
    type: Optional[str] = None
    message: Optional[str] = None


class ExecResp(BaseModel):
    ok: bool
    stdout: str = ""
    stderr: str = ""
    returncode: Optional[int] = None
    error: Optional[ErrorInfo] = None
    timeout: bool = False


class RepairReq(BaseModel):
    prompt: str
    last_error: Optional[ErrorInfo] = None
    expected_function_name: Optional[str] = None
    max_new_tokens: int = 256
    # 可选：在修复后立刻验证
    test_snippet: Optional[str] = None
    invoke: Optional[InvokeSpec] = None
    timeout_sec: int = 10


class RepairResp(BaseModel):
    code: str
    valid_syntax: bool
    looks_like_function: bool
    exec_result: Optional[ExecResp] = None


def _parse_error(stderr: str) -> ErrorInfo:
    """从 stderr 中粗略提取错误类型和消息（最后一条报错）。"""
    if not stderr:
        return ErrorInfo()
    # 取最后一行类似：ValueError: message
    m = None
    for line in stderr.strip().splitlines()[::-1]:
        m = re.search(r"([A-Za-z_][A-Za-z0-9_]*Error):\s*(.*)", line)
        if m:
            break
    if m:
        return ErrorInfo(type=m.group(1), message=m.group(2))
    return ErrorInfo()


def _run_in_subprocess(py_content: str, timeout_sec: int) -> ExecResp:
    """在隔离子进程中执行给定 Python 文本，返回执行结果。"""
    with tempfile.NamedTemporaryFile("w", delete=False, suffix=".py", encoding="utf-8") as tf:
        tf.write(py_content)
        temp_path = tf.name
    try:
        proc = subprocess.run([sys.executable, temp_path], capture_output=True, text=True, timeout=timeout_sec)
        ok = proc.returncode == 0
        err = _parse_error(proc.stderr)
        return ExecResp(
            ok=ok,
            stdout=proc.stdout or "",
            stderr=proc.stderr or "",
            returncode=proc.returncode,
            error=err if not ok else None,
            timeout=False,
        )
    except subprocess.TimeoutExpired as te:
        return ExecResp(
            ok=False,
            stdout=te.stdout or "",
            stderr=(te.stderr or "") + "\n[Timeout] Execution exceeded limit",
            returncode=None,
            error=ErrorInfo(type="TimeoutError", message="Execution time limit exceeded"),
            timeout=True,
        )
    finally:
        try:
            os.remove(temp_path)
        except Exception:
            pass


@app.post("/generate", response_model=GenResp)
async def api_generate(r: GenReq, current_user: dict = Depends(get_current_user)):
    """生成代码 - 使用模板引导AI生成(需要登录)"""
    
    # 先检查是否有匹配的模板
    template_code = get_template_for_prompt(r.prompt)
    
    # 策略：使用模板作为 Few-Shot 示例引导 AI 生成
    if template_code:
        print(f"[INFO] ✅ 找到匹配模板 (长度: {len(template_code)} 字符)")
        print(f"[INFO] 🤖 调用 AI 生成，使用模板作为示例引导...")
        code = generate_code(
            r.prompt, 
            max_new_tokens=r.max_new_tokens, 
            mode=(r.mode or "auto"),
            template_guide=template_code  # 传入模板作为参考
        )
        print(f"[INFO] 📝 AI 生成完成 (长度: {len(code)} 字符)")
        
        # 比较 AI 生成的代码和模板是否完全相同
        if code.strip() == template_code.strip():
            print(f"[INFO] ⚠️  AI 生成的代码与模板完全相同!")
        else:
            print(f"[INFO] ✨ AI 生成的代码与模板不同 (这是AI真正生成的!)")
            print(f"[DEBUG] AI 生成的代码预览:")
            print("=" * 60)
            print(code[:500] if len(code) > 500 else code)
            print("=" * 60)
    else:
        print(f"[INFO] ❌ 无匹配模板，使用纯 AI 生成")
        code = generate_code(r.prompt, max_new_tokens=r.max_new_tokens, mode=(r.mode or "auto"))
    
    is_valid = validate_python_code(code)
    is_function = looks_like_function(code)
    
    print(f"[INFO] 验证结果: valid={is_valid}, is_function={is_function}")
    
    if not is_valid:
        print(f"[DEBUG] ❌ 语法验证失败原因:")
        try:
            compile(code, '<string>', 'exec')
        except SyntaxError as e:
            print(f"  SyntaxError: {e}")
    
    if not is_function:
        print(f"[DEBUG] ❌ 函数结构验证失败 - 代码可能包含非函数内容")
    
    # 兜底策略：如果 AI 生成失败且有模板，直接返回模板
    if template_code and (not is_valid or not is_function):
        print(f"[WARN] ⛑️  AI 生成失败，回退到模板")
        code = template_code
        is_valid = validate_python_code(code)
        is_function = looks_like_function(code)
    
    # 保存到历史记录
    conv_id = None
    if r.save_history:
        conv_id = save_conversation(
            user_id=current_user["id"],
            prompt=r.prompt,
            generated_code=code,
            is_valid=is_valid and is_function
        )
    
    return GenResp(
        code=code,
        valid_syntax=is_valid,
        looks_like_function=is_function,
        conversation_id=conv_id
    )


# /analyze-quality 已移除（评分逻辑删除）


@app.post("/explain-code")
async def explain_code_endpoint(code: str, current_user: dict = Depends(get_current_user)):
    """使用AST分析解释代码结构和逻辑"""
    explanation = explain_code(code)
    return {"explanation": explanation}


class VariantRequest(BaseModel):
    prompt: str
    max_new_tokens: int = 256


class CodeVariant(BaseModel):
    code: str
    style: str
    description: str
    # 不再返回质量评分


@app.post("/generate-variants")
async def generate_code_variants(r: VariantRequest, current_user: dict = Depends(get_current_user)):
    """生成代码的多个变体实现"""
    variants = []
    
    # 变体1: 标准实现
    code1 = generate_code(r.prompt, max_new_tokens=r.max_new_tokens)
    variants.append({
        "code": code1,
        "style": "🎯 标准实现",
        "description": "清晰易懂的标准写法"
    })
    
    # 变体2: 简洁实现 (使用列表推导/lambda等)
    prompt2 = r.prompt + ". Use list comprehension or lambda if possible for a concise solution"
    code2 = generate_code(prompt2, max_new_tokens=r.max_new_tokens)
    if code2 != code1:  # 确保不重复
        variants.append({
            "code": code2,
            "style": "⚡ 简洁实现",
            "description": "使用Python高级特性,代码更简洁"
        })
    
    # 变体3: 详细实现 (添加注释和类型注解)
    prompt3 = r.prompt + ". Include type hints and clear variable names"
    code3 = generate_code(prompt3, max_new_tokens=r.max_new_tokens)
    if code3 != code1 and code3 != code2:
        variants.append({
            "code": code3,
            "style": "📚 详细实现",
            "description": "包含类型注解,变量命名清晰"
        })
    
    return {"variants": variants, "total": len(variants)}


@app.post("/execute", response_model=ExecResp)
async def api_execute(r: ExecReq):
    # 语法初检
    if not validate_python_code(r.code):
        return ExecResp(
            ok=False,
            stdout="",
            stderr="SyntaxError: invalid syntax",
            returncode=1,
            error=ErrorInfo(type="SyntaxError", message="invalid syntax"),
        )

    py_content = r.code.strip() + "\n\n"
    if r.test_snippet:
        py_content += r.test_snippet.strip() + "\n"
    elif r.invoke and r.invoke.function_name:
        args_json = json.dumps(r.invoke.args or [])
        py_content += (
            "import json\n"
            f"_args = json.loads(r'''{args_json}''')\n"
            f"_res = {r.invoke.function_name}(*_args)\n"
            "print(_res)\n"
        )
    else:
        return ExecResp(
            ok=False,
            stdout="",
            stderr="No test_snippet or invoke provided",
            returncode=1,
            error=ErrorInfo(type="ValueError", message="No test_snippet or invoke provided"),
        )

    return _run_in_subprocess(py_content, timeout_sec=r.timeout_sec)


def _make_repair_prompt(base_prompt: str, last_error: Optional[ErrorInfo], expected_fn: Optional[str]) -> str:
    p = base_prompt.strip()
    lines = []
    # 函数名约束优先
    if expected_fn:
        # 兼容中英文
        if any("\u4e00" <= ch <= "\u9fff" for ch in p):
            lines.append(f"函数名必须为 '{expected_fn}'。")
        else:
            lines.append(f"Function name must be '{expected_fn}'.")
    if last_error and (last_error.type or last_error.message):
        err_txt = f"{last_error.type or 'Error'}: {last_error.message or ''}".strip()
        # 兼容中英文
        if any("\u4e00" <= ch <= "\u9fff" for ch in p):
            lines.append(f"上次代码错误：{err_txt}。请修复，并仅返回一个完整的 Python 函数。")
        else:
            lines.append(f"Previous error: {err_txt}. Please fix it and return only one complete Python function.")
    return p + ("\n" + "\n".join(lines) if lines else "")


@app.post("/repair", response_model=RepairResp)
async def api_repair(r: RepairReq):
    prompt2 = _make_repair_prompt(r.prompt, r.last_error, r.expected_function_name)
    code = generate_code(prompt2, max_new_tokens=r.max_new_tokens)
    resp = RepairResp(
        code=code,
        valid_syntax=validate_python_code(code),
        looks_like_function=looks_like_function(code),
        exec_result=None,
    )
    # 可选：立即运行验证
    if r.test_snippet or r.invoke:
        exec_req = ExecReq(code=code, test_snippet=r.test_snippet, invoke=r.invoke, timeout_sec=r.timeout_sec)
        resp.exec_result = await api_execute(exec_req)  # 直接复用
    return resp


# ===================== #
# 成就系统API
# ===================== #

@app.get("/stats")
async def get_stats(current_user: dict = Depends(get_current_user)):
    """获取用户统计数据"""
    stats = get_user_stats(current_user["id"])
    
    # 计算平均分
    avg_score = 0
    if stats["total_generations"] > 0:
        avg_score = round(stats["total_score_sum"] / stats["total_generations"], 1)
    
    return {
        "total_generations": stats["total_generations"],
        "perfect_scores": stats["perfect_scores"],
        "good_scores": stats["good_scores"],
        "average_score": avg_score,
        "consecutive_days": stats["consecutive_days"],
        "last_active_date": stats["last_active_date"]
    }


@app.get("/achievements")
async def get_achievements(current_user: dict = Depends(get_current_user)):
    """获取用户成就列表"""
    achievements = get_user_achievements(current_user["id"])
    
    # 所有可能的成就
    all_achievements = [
        {"type": "first_code", "name": "🎓 初来乍到", "description": "生成第一个代码"},
        {"type": "code_master_10", "name": "🚀 代码新手", "description": "累计生成10个代码"},
        {"type": "code_master_50", "name": "💻 代码达人", "description": "累计生成50个代码"},
        {"type": "code_master_100", "name": "🏆 代码大师", "description": "累计生成100个代码"},
        {"type": "perfectionist", "name": "💯 完美主义者", "description": "获得5个满分代码"},
        {"type": "super_perfectionist", "name": "⭐ 超级完美", "description": "获得20个满分代码"},
        {"type": "consecutive_3", "name": "🔥 坚持不懈", "description": "连续3天使用"},
        {"type": "consecutive_7", "name": "📅 一周连续", "description": "连续7天使用"},
        {"type": "first_perfect", "name": "✨ 首个满分", "description": "获得第一个满分代码"},
    ]
    
    unlocked_types = [a["achievement_type"] for a in achievements]
    
    for ach in all_achievements:
        ach["unlocked"] = ach["type"] in unlocked_types
        if ach["unlocked"]:
            unlock_info = next(a for a in achievements if a["achievement_type"] == ach["type"])
            ach["unlocked_at"] = unlock_info["unlocked_at"]
    
    return {
        "achievements": all_achievements,
        "total": len(all_achievements),
        "unlocked": len(unlocked_types)
    }


if __name__ == "__main__":
    # 初始化数据库
    init_db()
    print("[INFO] 启动API服务器...")
    uvicorn.run(app, host="0.0.0.0", port=8000)
